(* Mathematica package *)

Clear[CrearCromosoma, CombinarCromosoma, MutarCromosoma, FuncionEvaluar, Evolucionar]

CrearCromosoma::usage = "Crea un cromosoma usando el comando CrearCromosoma[tam, rango] donde tam es el tamano del 
cromosoma y rango es el rango de donde se tomaran los numeros aleatorios para el cromosoma"
CrearCromosoma[tam_, rango_] := Block[
  {},
  Cromosoma[SinEvaluar, Table[Random[Real, rango], {tam}]]
  ]
  
  

CrearCromosomaEvaluado[eval_, cromo_] := Cromosoma[eval, cromo]
CombinarCromosoma[cr1_, cr2_] := Block[
  {punto = Random[Integer, {1, Length[cr1[[2]]]}]},
  {
   Cromosoma[SinEvaluar, 
    Join[Take[cr1[[2]], {1, punto}], 
     Take[cr2[[2]], {punto + 1, (Length[cr1[[2]]])}]]],
   Cromosoma[SinEvaluar, 
    Join[Take[cr2[[2]], {1, punto}], 
     Take[cr1[[2]], {punto + 1, (Length[cr1[[2]]])}]]]
   }
  ]
CrearPoblacion[nPob_, tamCromosoma_, rangoCromosoma_] := Block[
  {},
  ParallelTable[CrearCromosoma[tamCromosoma, rangoCromosoma], {nPob}]
  ]
CrearPoblacion2[nPob_, tamCromosoma_, rangoCromosoma_] := Block[
  {},
  Table[CrearCromosoma[tamCromosoma, rangoCromosoma], {nPob}]
  ]
EvaluarCromosoma[poblacion_, fEvaluar_] := 
  ParallelMap[fEvaluar, poblacion];
MutarCromosoma[cr_] := Block[
  {cromo = cr[[2]]},
  cromo[[Random[Integer, {1, Length[cr[[2]]]}]]] += 
   Random[Real, {0, 1}];
  Cromosoma[SinEvaluar, cromo]
  ]
FuncionEvaluar[Cromosoma[eval_, cromo_]] := Block[
   {},
   If[eval === SinEvaluar, Total[cromo], eval]
   ];
Evolucionar[padres_, hijos_, generaciones_, tamCromo_, rangoCromo_, 
   funcionEval_] := Block[
   {pob = CrearPoblacion[padres, tamCromo, rangoCromo], 
    evals = {},
    nuevoshijos = {}
    },
   For[j = 1, j <= generaciones, j++,
    Block[
     {},
     evals = EvaluarCromosoma[pob, funcionEval];
     DistributeDefinitions[evals, pob];
     pob = 
      Table[Cromosoma[evals[[k]], pob[[k, 2]]], {k, Length[evals]}];
     
     (*
     For[i=1, i<= Length[pob],i++,
     pob[[i]][[1]]=evals[[i]];
     ];
     *)
     pob = Sort[pob];
     nuevoshijos = {};
     
     For[i = 1, i <= hijos, i = i + 2,
      nuevoshijos = 
        Join[nuevoshijos, CombinarCromosoma[pob[[i]], pob[[i + 1]]]];
      ];
     
     pob = 
      Join[Take[pob, padres - hijos - 1], {MutarCromosoma[pob[[1]]]}, 
       Take[nuevoshijos, hijos]];
     ]
    ];
   pob
   ];
DistributeDefinitions[CrearCromosoma, CombinarCromosoma, \
MutarCromosoma, FuncionEvaluar, Evolucionar]
